﻿// ***********************************************************************************************************************************************************
// * Copyright (c) 2008 Unit Software                                                                                                                        *
// *                                                                                                                                                         *
// * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),      *
// * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,      *
// * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:              *
// *                                                                                                                                                         *
// * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.                          *
// *                                                                                                                                                         *
// * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,     *
// * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER      *
// * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS    *
// * IN THE SOFTWARE.                                                                                                                                        *
// ***********************************************************************************************************************************************************
using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Linq;

namespace UnitSoftware.LINQ2SQLExtensions
{
    public class TransientDataContextWrapper
    {
        #region Public 

        public event EventHandler<DataChangesEventArgs> OnPostExecute;

        public readonly ExtendedDataContext DataContext;

        public TransientDataContextWrapper(ExtendedDataContext dataContext)
        {
            DataContext = dataContext;

            TransientDataContextWrapperDirector.INSTANCE.RegisterNew(this);
        }

        public void RegisterNew<TEntity>(TEntity entity) where TEntity : Entity
        {
            _operationStack.StackOperation(new DataContextOperationStack.NewEntityOperation<TEntity>(entity));

            object[] attrs;
            foreach (var info in entity.GetType().GetProperties())
                if ((attrs = info.GetCustomAttributes(typeof (ColumnAttribute), true)).Length > 0 &&
                    !((ColumnAttribute) attrs[0]).IsPrimaryKey)
                    _operationStack.StackOperation(new DataContextOperationStack.PropertyChangedOperation(entity,
                                                                                                          info.Name));

            RegisterForChanges(entity);
        }

        public void RegisterForChanges<TEntity>(TEntity entity) where TEntity : Entity
        {
            lock (_propertyChangeRegistrationLock)
            {
                if (!_propertyChangeRegistrations.ContainsKey(entity))
                    _propertyChangeRegistrations.Add(entity,
                                                     new PropertyChangeRegistration<TEntity>(entity, _operationStack));
            }
        }

        public void Persist<TEntity>(TEntity entity) where TEntity : Entity
        {
            var table = ((Table<TEntity>)DataContext.GetTable(entity.TableType));

            if (!table.Contains(entity))
            {
                table.InsertOnSubmit(entity);
                _operationStack.StackOperation(entity, Operation.Insert);
                RegisterForChanges(entity);
            }
        }

        public void Delete<TEntity>(TEntity entity) where TEntity : Entity
        {
            var table = ((Table<TEntity>)DataContext.GetTable(entity.TableType));

            if (table.Contains(entity))
            {
                table.DeleteOnSubmit(entity);
                _operationStack.StackOperation(entity, Operation.Delete);
            }
        }

        public DataChanges Execute(ExtendedDataContext context)
        {
            var changes = _operationStack.Execute(context);

            if (OnPostExecute != null)
                OnPostExecute(this, new DataChangesEventArgs(context, changes));

            return changes;
        }

        #endregion

        #region Private

        private readonly DataContextOperationStack _operationStack = new DataContextOperationStack();
        private readonly object _propertyChangeRegistrationLock = new object();
        private readonly Dictionary<Entity, object> _propertyChangeRegistrations = new Dictionary<Entity, object>();

        #endregion
    }
}